﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using PD.DataAccess;
using PD.WorkflowLibrary.ExternalData;
using PD.WorkflowLibrary.Properties;

namespace PD.WorkflowLibrary
{
    /// <summary>
    /// Represents a workflow that sequentially executes activities to process the invoice.
    /// </summary>
    public partial class InvoiceProcessingWorkflow : SequentialWorkflowActivity
    {
        #region Fields

        /// <summary>
        /// The handle external event activity correct invoice event args.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1306:FieldNamesMustBeginWithLowerCaseLetter", Justification = "A dependency property must be static and start with uppercase.")]
        private static DependencyProperty HandleExternalEventActivityCorrectInvoiceEventArgsProperty = DependencyProperty.Register("HandleExternalEventActivityCorrectInvoiceEventArgs", typeof(TaskUpdatedEventArgs), typeof(InvoiceProcessingWorkflow));

        /// <summary>
        /// The handle external event activity confirm invoice event args.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1306:FieldNamesMustBeginWithLowerCaseLetter", Justification = "A dependency property must be static and start with uppercase.")]
        private static DependencyProperty HandleExternalEventActivityConfirmInvoiceEventArgsProperty = DependencyProperty.Register("HandleExternalEventActivityConfirmInvoiceEventArgs", typeof(TaskUpdatedEventArgs), typeof(InvoiceProcessingWorkflow));

        /// <summary>
        /// The handle external event activity approve invoice event args.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1306:FieldNamesMustBeginWithLowerCaseLetter", Justification = "A dependency property must be static and start with uppercase.")]
        private static DependencyProperty HandleExternalEventActivityConfirmInvoiceByChiefAccountantEventArgsProperty = DependencyProperty.Register("HandleExternalEventActivityConfirmInvoiceByChiefAccountantEventArgs", typeof(TaskUpdatedEventArgs), typeof(InvoiceProcessingWorkflow));

        /// <summary>
        /// The handle external event activity approve invoice event args.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1306:FieldNamesMustBeginWithLowerCaseLetter", Justification = "A dependency property must be static and start with uppercase.")]
        private static DependencyProperty HandleExternalEventActivityConfirmInvoiceByCEOEventArgsProperty = DependencyProperty.Register("HandleExternalEventActivityConfirmInvoiceByCEOEventArgs", typeof(TaskUpdatedEventArgs), typeof(InvoiceProcessingWorkflow));

        /// <summary>
        /// The handle external event activity final approval event args.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1306:FieldNamesMustBeginWithLowerCaseLetter", Justification = "A dependency property must be static and start with uppercase.")]
        private static DependencyProperty HandleExternalEventActivityFinalConfirmationEventArgsProperty = DependencyProperty.Register("HandleExternalEventActivityFinalConfirmationEventArgs", typeof(TaskUpdatedEventArgs), typeof(InvoiceProcessingWorkflow));

        /// <summary>
        /// The handle external event activity archive invoice event args.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1306:FieldNamesMustBeginWithLowerCaseLetter", Justification = "A dependency property must be static and start with uppercase.")]
        private static DependencyProperty HandleExternalEventActivityInvoiceHasBeenArchivedEventArgsProperty = DependencyProperty.Register("HandleExternalEventActivityInvoiceHasBeenArchivedEventArgs", typeof(TaskUpdatedEventArgs), typeof(InvoiceProcessingWorkflow));

        /// <summary>
        /// The handle external event activity fault event args.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1306:FieldNamesMustBeginWithLowerCaseLetter", Justification = "A dependency property must be static and start with uppercase.")]
        private static DependencyProperty ExceptionHandlerActivityFaultProperty = DependencyProperty.Register("ExceptionHandlerActivityFault", typeof(Exception), typeof(InvoiceProcessingWorkflow));

        /// <summary>
        /// The identifier of provide correct info task.
        /// </summary>
        private int provideCorrectInfoTaskId;

        /// <summary>
        /// The identifier of confirmation task.
        /// </summary>
        private int confirmInvoiceTaskId;

        /// <summary>
        /// The identifier of approval by chief accountant task.
        /// </summary>
        private int approvalByChiefAccountantTaskId;

        /// <summary>
        /// The identifier of approval by CEO task.
        /// </summary>
        private int approvalByCEOTaskId;

        /// <summary>
        /// The identifier of final approval task.
        /// </summary>
        private int finalApprovalTaskId;

        /// <summary>
        /// The identifier of the archieved task.
        /// </summary>
        private int archivedTaskId;

        #endregion

        #region Properties

        #region Workflow's Parameters

        /// <summary>
        /// Gets or sets the identifier of user who started the workflow.
        /// </summary>
        /// <value>
        /// The identifier of user who started the workflow.
        /// </value>
        public Guid Issuer { get; set; }

        /// <summary>
        /// Gets or sets the identifier of the supervisor employee.
        /// </summary>
        /// <value>
        /// The identifier of the supervisor employee.
        /// </value>
        public Guid InvolvedSupervisor { get; set; }

        /// <summary>
        /// Gets or sets the identifier of the involved accountant employee.
        /// </summary>
        /// <value>
        /// The identifier of the involved accountant employee.
        /// </value>
        public Guid InvolvedAccountant { get; set; }

        /// <summary>
        /// Gets or sets the invoice id.
        /// </summary>
        /// <value>
        /// The invoice id.
        /// </value>
        public int InvoiceId { get; set; }

        #endregion

        #region Condition Properties

        /// <summary>
        /// Gets a value indicating whether the invoice info is correct.
        /// </summary>
        /// <value>
        /// <c>true</c> if the invoice info is correct; otherwise, <c>false</c>.
        /// </value>
        public bool InvoiceInfoIsCorrect
        {
            get
            {
                using (PDDataClassesDataContext context = new PDDataClassesDataContext())
                {
                    Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                    return invoice != null ? invoice.Status == InvoiceStatus.Correct : false;
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether the invoice has been confirmed.
        /// </summary>
        /// <value>
        /// <c>true</c> if the invoice has been confirmed; otherwise, <c>false</c>.
        /// </value>
        public bool InvoiceHasBeenConfirmed
        {
            get { return this.CheckTaskStatus(this.confirmInvoiceTaskId, TaskStatus.Approved); }
        }

        /// <summary>
        /// Gets a value indicating whether the invoice has been approved by chief accountant.
        /// </summary>
        /// <value>
        /// <c>true</c> if the invoice has been approved by chief accountant; otherwise, <c>false</c>.
        /// </value>
        public bool InvoiceHasBeenApprovedByChiefAccountant
        {
            get { return this.CheckTaskStatus(this.approvalByChiefAccountantTaskId, TaskStatus.Approved); }
        }

        /// <summary>
        /// Gets a value indicating whether the invoice has been approved by CEO.
        /// </summary>
        /// <value>
        /// <c>true</c> if invoice has been approved by CEO; otherwise, <c>false</c>.
        /// </value>
        public bool InvoiceHasBeenApprovedByCEO
        {
            get { return this.CheckTaskStatus(this.approvalByCEOTaskId, TaskStatus.Approved); }
        }

        /// <summary>
        /// Gets a value indicating whether the invoice has been finally approved.
        /// </summary>
        /// <value>
        /// <c>true</c> if the invoice has been finally approved; otherwise, <c>false</c>.
        /// </value>
        public bool InvoiceHasBeenFinallyApproved
        {
            get { return this.CheckTaskStatus(this.finalApprovalTaskId, TaskStatus.Approved); }
        }

        /// <summary>
        /// Gets a value indicating whether the invoice has been archieved.
        /// </summary>
        /// <value>
        /// <c>true</c> if the invoice has been archieved; otherwise, <c>false</c>.
        /// </value>
        public bool InvoiceHasBeenArchieved
        {
            get { return this.CheckTaskStatus(this.archivedTaskId, TaskStatus.Approved); }
        }

        /// <summary>
        /// Gets a value indicating whether the invoice's amount value is less than first threshold.
        /// </summary>
        /// <value>
        /// <c>true</c> if the invoice's amount value is less than first threshold; otherwise, <c>false</c>.
        /// </value>
        public bool AmountIsLessThanFirstThreshold
        {
            get
            {
                using (PDDataClassesDataContext context = new PDDataClassesDataContext())
                {
                    Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                    if (invoice != null)
                    {
                        return invoice.TotalAmount < HttpContext.Current.Configuration().FirstAcceptanceThreshold;
                    }

                    throw new InvalidOperationException();
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether the invoice's amount value is greater than second threshold.
        /// </summary>
        /// <value>
        /// <c>true</c> if the invoice's amount value is greater than second threshold; otherwise, <c>false</c>.
        /// </value>
        public bool AmountIsGreaterThanSecondThreshold
        {
            get
            {
                using (PDDataClassesDataContext context = new PDDataClassesDataContext())
                {
                    Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                    if (invoice != null)
                    {
                        return invoice.TotalAmount > HttpContext.Current.Configuration().SecondAcceptanceThreshold;
                    }
                }

                throw new InvalidOperationException();
            }
        }

        #endregion

        #region Dependency Properties

        /// <summary>
        /// Gets or sets the handle external event activity correct invoice event args.
        /// </summary>
        /// <value>
        /// The handle external event activity correct invoice event args.
        /// </value>
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        [BrowsableAttribute(true)]
        [CategoryAttribute("Parameters")]
        public TaskUpdatedEventArgs HandleExternalEventActivityCorrectInvoiceEventArgs
        {
            get
            {
                return (TaskUpdatedEventArgs)this.GetValue(InvoiceProcessingWorkflow.HandleExternalEventActivityCorrectInvoiceEventArgsProperty);
            }

            set
            {
                this.SetValue(InvoiceProcessingWorkflow.HandleExternalEventActivityCorrectInvoiceEventArgsProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the handle external event activity confirm invoice event args.
        /// </summary>
        /// <value>
        /// The handle external event activity confirm invoice event args.
        /// </value>
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        [BrowsableAttribute(true)]
        [CategoryAttribute("Parameters")]
        public TaskUpdatedEventArgs HandleExternalEventActivityConfirmInvoiceEventArgs
        {
            get
            {
                return (TaskUpdatedEventArgs)this.GetValue(PD.WorkflowLibrary.InvoiceProcessingWorkflow.HandleExternalEventActivityConfirmInvoiceEventArgsProperty);
            }

            set
            {
                this.SetValue(InvoiceProcessingWorkflow.HandleExternalEventActivityConfirmInvoiceEventArgsProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the handle external event activity confirm invoice by chief accountant event args.
        /// </summary>
        /// <value>
        /// The handle external event activity confirm invoice by chief accountant event args.
        /// </value>
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        [BrowsableAttribute(true)]
        [CategoryAttribute("Parameters")]
        public TaskUpdatedEventArgs HandleExternalEventActivityConfirmInvoiceByChiefAccountantEventArgs
        {
            get
            {
                return (TaskUpdatedEventArgs)this.GetValue(InvoiceProcessingWorkflow.HandleExternalEventActivityConfirmInvoiceByChiefAccountantEventArgsProperty);
            }

            set
            {
                this.SetValue(InvoiceProcessingWorkflow.HandleExternalEventActivityConfirmInvoiceByChiefAccountantEventArgsProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the handle external event activity confirm invoice by CEO event args.
        /// </summary>
        /// <value>
        /// The handle external event activity confirm invoice by CEO event args.
        /// </value>
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        [BrowsableAttribute(true)]
        [CategoryAttribute("Parameters")]
        public TaskUpdatedEventArgs HandleExternalEventActivityConfirmInvoiceByCEOEventArgs
        {
            get
            {
                return (TaskUpdatedEventArgs)this.GetValue(InvoiceProcessingWorkflow.HandleExternalEventActivityConfirmInvoiceByCEOEventArgsProperty);
            }

            set
            {
                this.SetValue(InvoiceProcessingWorkflow.HandleExternalEventActivityConfirmInvoiceByCEOEventArgsProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the handle external event activity final confirmation event args.
        /// </summary>
        /// <value>
        /// The handle external event activity final confirmation event args.
        /// </value>
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        [BrowsableAttribute(true)]
        [CategoryAttribute("Parameters")]
        public TaskUpdatedEventArgs HandleExternalEventActivityFinalConfirmationEventArgs
        {
            get
            {
                return (TaskUpdatedEventArgs)this.GetValue(InvoiceProcessingWorkflow.HandleExternalEventActivityFinalConfirmationEventArgsProperty);
            }

            set
            {
                this.SetValue(InvoiceProcessingWorkflow.HandleExternalEventActivityFinalConfirmationEventArgsProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the handle external event activity invoice has been archived event args.
        /// </summary>
        /// <value>
        /// The handle external event activity invoice has been archived event args.
        /// </value>
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        [BrowsableAttribute(true)]
        [CategoryAttribute("Parameters")]
        public TaskUpdatedEventArgs HandleExternalEventActivityInvoiceHasBeenArchivedEventArgs
        {
            get
            {
                return (TaskUpdatedEventArgs)this.GetValue(PD.WorkflowLibrary.InvoiceProcessingWorkflow.HandleExternalEventActivityInvoiceHasBeenArchivedEventArgsProperty);
            }

            set
            {
                this.SetValue(InvoiceProcessingWorkflow.HandleExternalEventActivityInvoiceHasBeenArchivedEventArgsProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the exception handler activity fault.
        /// </summary>
        /// <value>
        /// The exception handler activity fault.
        /// </value>
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
        [BrowsableAttribute(true)]
        [CategoryAttribute("Misc")]
        public Exception ExceptionHandlerActivityFault
        {
            get
            {
                return (Exception)this.GetValue(InvoiceProcessingWorkflow.ExceptionHandlerActivityFaultProperty);
            }

            set
            {
                this.SetValue(InvoiceProcessingWorkflow.ExceptionHandlerActivityFaultProperty, value);
            }
        }

        #endregion

        #endregion

        #region Handlers

        /// <summary>
        /// Handles the ExecuteCode event of the TrackInvoiceRequest control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void TrackInvoiceRequest_ExecuteCode(object sender, EventArgs e)
        {
            this.SetWorkflowStatus(WorkflowStatus.InProgress);
            this.TrackData(TrackDataKeys.InvoiceState, InvoiceState.WorkflowStarted);
        }

        /// <summary>
        /// Handles the ExecuteCode event of the TrackIncorrectInvoice control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void TrackIncorrectInvoice_ExecuteCode(object sender, EventArgs e)
        {
            // faktura niepoprawna
            // dodaj taska do issuera i czekaj na poprawe
            this.TrackData(TrackDataKeys.InvoiceState, InvoiceState.InvoiceIncorrectAndWaitingForCorrectInfo);

            using (PDDataClassesDataContext context = new PDDataClassesDataContext())
            {
                context.EventFiringEnabled = false;
                Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                if (invoice != null)
                {
                    Task task = new Task();
                    task.AssignedTo = this.Issuer;
                    task.StartDate = DateTime.Now;
                    task.DueDate = task.StartDate.AddDays(Settings.Default.TaskDeadline);
                    task.InvoiceId = invoice.InvoiceId;
                    task.PercentageCompleted = 0;
                    task.Priority = TaskPriority.High;
                    task.Status = TaskStatus.NotStarted;
                    task.Title = Settings.Default.CorrectInfoTask_Title;
                    task.Description = string.Format(
                        CultureInfo.CurrentCulture,
                        Settings.Default.CorrectInfoTask_Description,
                        invoice.Title,
                        string.Format("{0:C}", invoice.TotalAmount));
                    invoice.Status = InvoiceStatus.WaitingForCorrectInfo;
                    context.Tasks.InsertOnSubmit(task);
                    context.SubmitChanges();
                    this.provideCorrectInfoTaskId = task.TaskId;
                }
            }
        }

        /// <summary>
        /// Handles the Invoked event of the HandleExternalEventActivityCorrectInvoice control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Workflow.Activities.ExternalDataEventArgs"/> instance containing the event data.</param>
        private void HandleExternalEventActivityCorrectInvoice_Invoked(object sender, ExternalDataEventArgs e)
        {
            TaskUpdatedEventArgs args = this.HandleExternalEventActivityCorrectInvoiceEventArgs;
            this.ProcessExternalEvent(args.TaskId, this.provideCorrectInfoTaskId, args.ModifiedBy, this.TrackIncorrectInvoice_ExecuteCode);
        }

        /// <summary>
        /// Handles the ExecuteCode event of the AddConfirmTask control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AddConfirmTask_ExecuteCode(object sender, EventArgs e)
        {
            // faktura jest poprawna
            // dodaj taska do odpowiedzialnego użytkownika, aby potwierdził
            this.TrackData(TrackDataKeys.InvoiceState, InvoiceState.ConfirmTaskAdded);
            using (PDDataClassesDataContext context = new PDDataClassesDataContext())
            {
                context.EventFiringEnabled = false;
                Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                if (invoice != null)
                {
                    Task task = new Task();
                    task.AssignedTo = this.InvolvedSupervisor;
                    task.StartDate = DateTime.Now;
                    task.DueDate = task.StartDate.AddDays(Settings.Default.TaskDeadline);
                    task.InvoiceId = invoice.InvoiceId;
                    task.PercentageCompleted = 0;
                    task.Priority = TaskPriority.Normal;
                    task.Status = TaskStatus.NotStarted;
                    task.Title = Settings.Default.ConfirmTask_Title;
                    task.Description = string.Format(
                        CultureInfo.CurrentCulture,
                        Settings.Default.ConfirmTask_Description,
                        invoice.Title,
                        string.Format("{0:C}", invoice.TotalAmount));
                    context.Tasks.InsertOnSubmit(task);
                    context.SubmitChanges();
                    this.confirmInvoiceTaskId = task.TaskId;
                }
            }
        }

        /// <summary>
        /// Handles the Invoked event of the HandleExternalEventActivityConfirmInvoice control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Workflow.Activities.ExternalDataEventArgs"/> instance containing the event data.</param>
        private void HandleExternalEventActivityConfirmInvoice_Invoked(object sender, ExternalDataEventArgs e)
        {
            TaskUpdatedEventArgs args = this.HandleExternalEventActivityConfirmInvoiceEventArgs;
            this.ProcessExternalEvent(args.TaskId, this.confirmInvoiceTaskId, args.ModifiedBy, this.AddConfirmTask_ExecuteCode);
        }

        /// <summary>
        /// Handles the ExecuteCode event of the TrackInvoiceConfirmed control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void TrackInvoiceConfirmed_ExecuteCode(object sender, EventArgs e)
        {
            this.TrackData(TrackDataKeys.InvoiceState, InvoiceState.InvoiceConfirmed);
        }

        /// <summary>
        /// Handles the ExecuteCode event of the AddApprovalByChiefAccountantTask control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AddApprovalByChiefAccountantTask_ExecuteCode(object sender, EventArgs e)
        {
            // kwota faktury jest większa niż pierwszy próg
            // dodaj taska dla głównej księgowej
            this.TrackData(TrackDataKeys.InvoiceState, InvoiceState.AwaitingForAprovalByChiefAccountant);
            using (PDDataClassesDataContext context = new PDDataClassesDataContext())
            {
                context.EventFiringEnabled = false;
                Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                if (invoice != null)
                {
                    Task task = new Task();
                    Config config = HttpContext.Current.Configuration();
                    task.AssignedTo = config.ChiefAccountant;
                    task.StartDate = DateTime.Now;
                    task.DueDate = task.StartDate.AddDays(Settings.Default.TaskDeadline);
                    task.InvoiceId = this.InvoiceId;
                    task.PercentageCompleted = 0;
                    task.Priority = TaskPriority.Normal;
                    task.Status = TaskStatus.NotStarted;
                    task.Title = Settings.Default.ApprovalByChiefAccountantTask_Title;
                    task.Description = string.Format(
                        CultureInfo.CurrentCulture,
                        Settings.Default.ApprovalByChiefAccountantTask_Description,
                        invoice.Title,
                        string.Format("{0:C}", invoice.TotalAmount),
                        string.Format("{0:C}", config.FirstAcceptanceThreshold));
                    context.Tasks.InsertOnSubmit(task);
                    context.SubmitChanges();
                    this.approvalByChiefAccountantTaskId = task.TaskId;
                }
            }
        }

        /// <summary>
        /// Handles the Invoked event of the HandleExternalEventActivityConfirmInvoiceByChiefAccountant control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Workflow.Activities.ExternalDataEventArgs"/> instance containing the event data.</param>
        private void HandleExternalEventActivityConfirmInvoiceByChiefAccountant_Invoked(object sender, ExternalDataEventArgs e)
        {
            TaskUpdatedEventArgs args = this.HandleExternalEventActivityConfirmInvoiceByChiefAccountantEventArgs;
            this.ProcessExternalEvent(args.TaskId, this.approvalByChiefAccountantTaskId, args.ModifiedBy, this.AddApprovalByChiefAccountantTask_ExecuteCode);
        }

        /// <summary>
        /// Handles the ExecuteCode event of the AddApprovalByCEOTask control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AddApprovalByCEOTask_ExecuteCode(object sender, EventArgs e)
        {
            // kwota faktury jest większa niż drugi próg
            // dodaj taska dla CEO
            this.TrackData(TrackDataKeys.InvoiceState, InvoiceState.AwaitingForAprovalByCEO);
            using (PDDataClassesDataContext context = new PDDataClassesDataContext())
            {
                context.EventFiringEnabled = false;
                Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                if (invoice != null)
                {
                    Task task = new Task();
                    Config config = HttpContext.Current.Configuration();
                    task.AssignedTo = config.CEO;
                    task.StartDate = DateTime.Now;
                    task.DueDate = task.StartDate.AddDays(Settings.Default.TaskDeadline);
                    task.InvoiceId = this.InvoiceId;
                    task.PercentageCompleted = 0;
                    task.Priority = TaskPriority.Normal;
                    task.Status = TaskStatus.NotStarted;
                    task.Title = Settings.Default.ApprovalByCEOTask_Title;
                    task.Description = string.Format(
                        CultureInfo.CurrentCulture,
                        Settings.Default.ApprovalByCEOTask_Description,
                        invoice.Title,
                        string.Format("{0:C}", invoice.TotalAmount),
                        string.Format("{0:C}", config.SecondAcceptanceThreshold));
                    context.Tasks.InsertOnSubmit(task);
                    context.SubmitChanges();
                    this.approvalByCEOTaskId = task.TaskId;
                }
            }
        }

        /// <summary>
        /// Handles the Invoked event of the HandleExternalEventActivityConfirmInvoiceByCEO control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Workflow.Activities.ExternalDataEventArgs"/> instance containing the event data.</param>
        private void HandleExternalEventActivityConfirmInvoiceByCEO_Invoked(object sender, ExternalDataEventArgs e)
        {
            TaskUpdatedEventArgs args = this.HandleExternalEventActivityConfirmInvoiceByCEOEventArgs;
            this.ProcessExternalEvent(args.TaskId, this.approvalByCEOTaskId, args.ModifiedBy, this.AddApprovalByCEOTask_ExecuteCode);
        }

        /// <summary>
        /// Handles the ExecuteCode event of the CheckingInvoiceAmountCompleted control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void CheckingInvoiceAmountCompleted_ExecuteCode(object sender, EventArgs e)
        {
            this.TrackData(TrackDataKeys.InvoiceState, InvoiceState.CheckingInvoiceAmountCompleted);
        }

        /// <summary>
        /// Handles the ExecuteCode event of the AddFinalApprovalTask control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AddFinalApprovalTask_ExecuteCode(object sender, EventArgs e)
        {
            this.TrackData(TrackDataKeys.InvoiceState, InvoiceState.AwaitingForFinalAproval);
            using (PDDataClassesDataContext context = new PDDataClassesDataContext())
            {
                context.EventFiringEnabled = false;
                Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                if (invoice != null)
                {
                    Task task = new Task();
                    Config config = HttpContext.Current.Configuration();
                    task.AssignedTo = config.ChiefAccountant;
                    task.StartDate = DateTime.Now;
                    task.DueDate = task.StartDate.AddDays(Settings.Default.TaskDeadline);
                    task.InvoiceId = this.InvoiceId;
                    task.PercentageCompleted = 0;
                    task.Priority = TaskPriority.Normal;
                    task.Status = TaskStatus.NotStarted;
                    task.Title = Settings.Default.FinalApprovalTask_Title;
                    task.Description = string.Format(
                        CultureInfo.CurrentCulture,
                        Settings.Default.FinalApprovalTask_Description,
                        invoice.Title,
                        string.Format("{0:C}", invoice.TotalAmount));
                    context.Tasks.InsertOnSubmit(task);
                    context.SubmitChanges();
                    this.finalApprovalTaskId = task.TaskId;
                }
            }
        }

        /// <summary>
        /// Handles the Invoked event of the HandleExternalEventActivityFinalConfirmation control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Workflow.Activities.ExternalDataEventArgs"/> instance containing the event data.</param>
        private void HandleExternalEventActivityFinalConfirmation_Invoked(object sender, ExternalDataEventArgs e)
        {
            TaskUpdatedEventArgs args = this.HandleExternalEventActivityFinalConfirmationEventArgs;
            this.ProcessExternalEvent(args.TaskId, this.finalApprovalTaskId, args.ModifiedBy, this.AddFinalApprovalTask_ExecuteCode);
        }

        /// <summary>
        /// Handles the ExecuteCode event of the AddWaitingForArchivedTask control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void AddWaitingForArchivedTask_ExecuteCode(object sender, EventArgs e)
        {
            this.TrackData(TrackDataKeys.InvoiceState, InvoiceState.AwaitingForArchived);
            using (PDDataClassesDataContext context = new PDDataClassesDataContext())
            {
                context.EventFiringEnabled = false;
                Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                if (invoice != null)
                {
                    Task task = new Task();
                    Config config = HttpContext.Current.Configuration();
                    task.AssignedTo = this.InvolvedAccountant;
                    task.StartDate = DateTime.Now;
                    task.DueDate = task.StartDate.AddDays(Settings.Default.TaskDeadline);
                    task.InvoiceId = this.InvoiceId;
                    task.PercentageCompleted = 0;
                    task.Priority = TaskPriority.Normal;
                    task.Status = TaskStatus.NotStarted;
                    task.Title = Settings.Default.AwaitingForArchivedTask_Title;
                    task.Description = string.Format(
                        CultureInfo.CurrentCulture,
                        Settings.Default.AwaitingForArchivedTask_Description,
                        invoice.Title,
                        string.Format("{0:C}", invoice.TotalAmount));
                    context.Tasks.InsertOnSubmit(task);
                    invoice.Status = InvoiceStatus.WaitingForArchived;
                    context.SubmitChanges();
                    this.archivedTaskId = task.TaskId;
                }
            }
        }

        /// <summary>
        /// Handles the Invoked event of the HandleExternalEventActivityInvoiceHasBeenArchived control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Workflow.Activities.ExternalDataEventArgs"/> instance containing the event data.</param>
        private void HandleExternalEventActivityInvoiceHasBeenArchived_Invoked(object sender, ExternalDataEventArgs e)
        {
            TaskUpdatedEventArgs args = this.HandleExternalEventActivityInvoiceHasBeenArchivedEventArgs;
            this.ProcessExternalEvent(args.TaskId, this.archivedTaskId, args.ModifiedBy, this.AddWaitingForArchivedTask_ExecuteCode);
        }

        /// <summary>
        /// Handles the ExecuteCode event of the InvoiceArchived control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void InvoiceArchived_ExecuteCode(object sender, EventArgs e)
        {
            this.TrackData(TrackDataKeys.InvoiceState, InvoiceState.Archived);
            using (PDDataClassesDataContext context = new PDDataClassesDataContext())
            {
                context.EventFiringEnabled = false;
                Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                if (invoice != null)
                {
                    invoice.Status = InvoiceStatus.Archived;
                    invoice.WorkflowStatus = WorkflowStatus.Approved;
                    context.SubmitChanges();
                }
            }
        }

        /// <summary>
        /// Handles the ExecuteCode event of the SetRejectedState control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void SetRejectedState_ExecuteCode(object sender, EventArgs e)
        {
            using (PDDataClassesDataContext context = new PDDataClassesDataContext())
            {
                context.EventFiringEnabled = false;
                Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                if (invoice != null)
                {
                    invoice.WorkflowStatus = WorkflowStatus.Rejected;
                    invoice.Status = InvoiceStatus.RejectedByWorkflow;
                    context.SubmitChanges();
                }
            }
        }

        /// <summary>
        /// Handles the ExecuteCode event of the LogExceptionActivity control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void LogExceptionActivity_ExecuteCode(object sender, EventArgs e)
        {
            Logger.Instance.Error("Workflow error.", this.ExceptionHandlerActivity.Fault);
            this.SetWorkflowStatus(WorkflowStatus.ErrorOccurred);
        }

        #endregion

        #region Helpers

        /// <summary>
        /// Sets the workflow status.
        /// </summary>
        /// <param name="status">The status.</param>
        private void SetWorkflowStatus(WorkflowStatus status)
        {
            using (PDDataClassesDataContext context = new PDDataClassesDataContext())
            {
                context.EventFiringEnabled = false;
                Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                if (invoice != null)
                {
                    invoice.WorkflowStatus = status;
                    context.SubmitChanges();
                }
            }
        }

        /// <summary>
        /// Processes the external event.
        /// </summary>
        /// <param name="taskId">The task id.</param>
        /// <param name="actionTaskId">The action task id.</param>
        /// <param name="modifiedBy">The identidier of the user who modified task.</param>
        /// <param name="addTaskHandler">The add task handler.</param>
        private void ProcessExternalEvent(int taskId, int actionTaskId, Guid modifiedBy, EventHandler addTaskHandler)
        {
            using (PDDataClassesDataContext context = new PDDataClassesDataContext())
            {
                Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                if (taskId == actionTaskId && invoice != null)
                {
                    context.EventFiringEnabled = false;
                    Task task = invoice.Tasks.FirstOrDefault(x => x.TaskId == taskId);
                    if (task != null)
                    {
                        if (task.Status == TaskStatus.Approved)
                        {
                            task.CompletedDate = DateTime.Now;
                            task.ModificationTime = task.CompletedDate;
                            task.PercentageCompleted = 100;
                            task.ModifiedBy = modifiedBy;
                            invoice.Status = InvoiceStatus.Correct;
                            context.SubmitChanges();
                        }
                        else if (task.Status == TaskStatus.Rejected)
                        {
                            // add task again
                            task.CompletedDate = DateTime.Now;
                            task.ModificationTime = task.CompletedDate;
                            task.PercentageCompleted = 100;
                            task.ModifiedBy = modifiedBy;
                            context.SubmitChanges();

                            throw new StopWorkflowException();
                            ////if (addTaskHandler != null)
                            ////{
                            ////    addTaskHandler.Invoke(null, EventArgs.Empty);
                            ////}
                        }
                        else
                        {
                            task.ModificationTime = DateTime.Now;
                            task.ModifiedBy = modifiedBy;
                            context.SubmitChanges();
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Checks the task status.
        /// </summary>
        /// <param name="taskId">The task id.</param>
        /// <param name="status">The status.</param>
        /// <returns>
        /// <c>true</c> if the task status matches given status; otherwise <c>false</c>.
        /// </returns>
        private bool CheckTaskStatus(int taskId, TaskStatus status)
        {
            using (PDDataClassesDataContext context = new PDDataClassesDataContext())
            {
                Invoice invoice = context.Invoices.FirstOrDefault(x => x.InvoiceId == this.InvoiceId);
                if (invoice != null)
                {
                    Task confirmTask = invoice.Tasks.FirstOrDefault(x => x.TaskId == taskId);
                    if (confirmTask != null)
                    {
                        return confirmTask.Status == status;
                    }
                }
            }

            return false;
        }

        #endregion
    }
}
